package org.sqlproc.engine;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.sqlproc.engine.impl.BeanUtils;
import org.sqlproc.engine.impl.SqlMappingResult;
import org.sqlproc.engine.impl.SqlMappingRule;
import org.sqlproc.engine.impl.SqlMetaStatement;
import org.sqlproc.engine.impl.SqlProcessResult;
import org.sqlproc.engine.impl.type.SqlMetaType;

/**
 * The primary SQL Processor class for the META SQL CRUD statement execution.
 * 
 * <p>
 * Instance of this class holds one META SQL statement.
 * <p>
 * For example there's a table PERSON with two columns - ID and NAME. <br>
 * In queries.properties there's the next definition:
 * 
 * <pre>
 * CRUD_UPDATE_PERSON= \
 *  update PERSON \
 *  {= set name = :name} \
 *  {= where {& id = :id^long^notnull}}
 * </pre>
 * 
 * <p>
 * In the case of SQL Processor initialization
 * 
 * <pre>
 * SqlPropertiesLoader loader = new SqlPropertiesLoader(&quot;queries.properties&quot;, this.getClass());
 * SqlEngineLoader sqlLoader = new SqlEngineLoader(loader.getProperties());
 * SqlCrudEngine sqlEngine = sqlLoader.getCrudEngine(&quot;UPDATE_PERSON&quot;);
 * </pre>
 * 
 * there's created an instance of SqlCrudEngine with the name <code>UPDATE_PERSON</code>.
 * 
 * <p>
 * Next the query can be executed with one of the <code>updateXXX</code> methods. For example there's a Java bean class
 * Person with attributes id and name. The invocation
 * 
 * <pre>
 * Person person = new Person();
 * person.setId(1);
 * person.setName(&quot;Bozena&quot;);
 * 
 * int count = sqlEngine.update(session, person);
 * </pre>
 * 
 * produces the next SQL execution
 * 
 * <pre>
 * update PERSON SET name = ? WHERE id = ?
 * </pre>
 * 
 * <p>
 * and returns the number of updated rows.
 * 
 * <p>
 * For more info please see the Reference Guide or <a
 * href="http://code.google.com/p/sql-processor/w/list">tutorials</a>.
 * 
 * @author <a href="mailto:Vladimir.Hudec@gmail.com">Vladimir Hudec</a>
 */
public class SqlCrudEngine extends SqlEngine {

    /**
     * Creates a new instance of SqlCrudEngine from one META SQL statement string. Constructor will call the internal
     * ANTLR parser for the CRUD statement construction. This constructor is devoted to manual META SQL statements
     * construction. More obvious is to put these definitions into queries.properties file and engage SqlEngineLoader
     * for the SqlCrudEngine instances construction.
     * 
     * @param name
     *            the name if this SQL Engine instance
     * @param statement
     *            the META SQL CRUD statement, extension of ANSI SQL
     * @throws SqlEngineException
     *             mainly in the case the provided statements are not compliant with the ANTLR grammar
     */
    public SqlCrudEngine(String name, String statement) throws SqlEngineException {

        super(name, SqlMetaStatement.getInstance(statement, SqlMetaType.META_TO_TYPE_MAP), null, null, null,
                SqlMetaType.CLASS_TO_TYPE_MAP);
    }

    /**
     * Creates a new instance of SqlCrudEngine from one META SQL statement string. Constructor will call the internal
     * ANTLR parser for the CRUD statement instances construction. Compared to the previous constructor, external SQL
     * Monitor for runtime statistics gathering is engaged. This constructor is devoted to manual META SQL statements
     * construction. More obvious is to put these statements into queries.properties file and engage SqlEngineLoader for
     * SqlEngine instances construction.
     * 
     * @param name
     *            the name if this SQL Engine instance
     * @param statement
     *            the META SQL CRUD statement, extension of ANSI SQL
     * @param monitor
     *            the SQL Monitor for the runtime statistics gathering
     * @param features
     *            the optional SQL Processor features
     * @param classToTypeMap
     *            the map between a Java class type and an internal type
     * @throws SqlEngineException
     *             mainly in the case the provided statements are not compliant with the ANTLR grammar
     */
    public SqlCrudEngine(String name, String statement, SqlMonitor monitor, Map<String, Object> features,
            Map<Class<?>, SqlMetaType> classToTypeMap) throws SqlEngineException {

        super(name, SqlMetaStatement.getInstance(statement, SqlMetaType.META_TO_TYPE_MAP), null, monitor, features,
                classToTypeMap);
    }

    /**
     * Creates a new instance of SqlCrudEngine from one META SQL statement instance. This instance is already
     * precompiled using the ANTLR parser. This is the recommended usage for the runtime performance optimization. This
     * constructor is devoted to be used from the SqlEngineLoader, which is able to read all definitions from an
     * external queries.properties and create the named SqlCrudEngine instances.
     * 
     * @param name
     *            the name if this SQL Engine instance
     * @param statement
     *            the precompiled META SQL CRUD statement, extension of ANSI SQL
     */
    public SqlCrudEngine(String name, SqlMetaStatement statement) {

        super(name, statement, null, null, null, SqlMetaType.CLASS_TO_TYPE_MAP);
    }

    /**
     * Creates a new instance of SqlCrudEngine from one META SQL statement instance. This instance is already
     * precompiled using the ANTLR parsers. This is the recommended usage for the runtime performance optimization. This
     * constructor is devoted to be used from the SqlEngineLoader, which is able to read all definitions from an
     * external queries.properties and create the named SqlCrudEngine instances. Compared to the previous constructor,
     * external SQL Monitor for the runtime statistics gathering is engaged.
     * 
     * @param name
     *            the name if this SQL Engine instance
     * @param statement
     *            the precompiled META SQL statement, extension of ANSI SQL
     * @param monitor
     *            the SQL Monitor for the runtime statistics gathering
     * @param features
     *            the optional SQL Processor features
     * @param classToTypeMap
     *            the map between a Java class type and an internal type
     */
    public SqlCrudEngine(String name, SqlMetaStatement statement, SqlMonitor monitor, Map<String, Object> features,
            Map<Class<?>, SqlMetaType> classToTypeMap) {
        super(name, statement, null, monitor, features, classToTypeMap);
    }

    /**
     * Runs a META SQL insert statement to persist a database row. This is one of the overriden methods. For the
     * parameters description please see the most complex execution method {@link #insert(Session, Object, Object, int)}
     * .
     */
    @SuppressWarnings("unchecked")
    public int insert(final Session session, final Object dynamicInputValues) throws HibernateException,
            SqlRuntimeException {
        return insert(session, dynamicInputValues, null, 0);
    }

    /**
     * Runs a META SQL insert statement to persist a database row. This is one of the overriden methods. For the
     * parameters description please see the most complex execution method {@link #insert(Session, Object, Object, int)}
     * .
     */
    @SuppressWarnings("unchecked")
    public int insert(final Session session, final Object dynamicInputValues, final Object staticInputValues)
            throws HibernateException, SqlRuntimeException {
        return insert(session, dynamicInputValues, staticInputValues, 0);
    }

    /**
     * Runs a META SQL insert statement to persist a database row. This is the primary and the most complex SQL
     * Processor execution method to persist an instance of input values. Inserted values are taken from input values.
     * 
     * @param session
     *            Hibernate session, first level cache and the SQL query execution context
     * @param dynamicInputValues
     *            The object used for the SQL statement dynamic parameters. The class of this object is also named as
     *            the input class or the dynamic parameters class. The exact class type isn't important, all the
     *            parameters substituted into the SQL prepared statement are picked up using the reflection API.
     * @param staticInputValues
     *            The object used for the SQL statement static parameters. The class of this object is also named as the
     *            input class or the static parameters class. The exact class type isn't important, all the parameters
     *            injected into the SQL query command are picked up using the reflection API. Compared to
     *            dynamicInputValues input parameters, parameters in this class should't be produced by the end user to
     *            prevent SQL injection threat!
     * @param maxTimeout
     *            The max SQL execution time. This parameter can help to protect production system against ineffective
     *            SQL query commands. The value is in milliseconds.
     * @return The number of persisted database rows.
     * @throws org.hibernate.HibernateException
     *             in the case of any problem with ORM or JDBC
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with input/output values handling
     */
    @SuppressWarnings("unchecked")
    public int insert(final Session session, final Object dynamicInputValues, final Object staticInputValues,
            final int maxTimeout) throws HibernateException, SqlRuntimeException {
        if (logger.isDebugEnabled()) {
            logger.debug(">> insert, session=" + session + ", dynamicInputValues=" + dynamicInputValues
                    + ", staticInputValues=" + staticInputValues + ", maxTimeout=" + maxTimeout);
        }

        Integer count = null;

        try {
            count = monitor.run(new SqlMonitor.Runner() {
                public Integer run() {
                    SqlProcessResult processResult = statement.process(SqlMetaStatement.Type.CREATE,
                            dynamicInputValues, staticInputValues, null, features, classToTypeMap);
                    SQLQuery query = session.createSQLQuery(processResult.getSql().toString());
                    if (maxTimeout > 0)
                        query.setTimeout(maxTimeout);
                    processResult.setQueryParams(session, query);

                    Integer count = query.executeUpdate();
                    processResult.postProcess();
                    return count;
                }
            }, Integer.class);
            return count;
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("<< insert, result=" + count);
            }
        }
    }

    /**
     * Runs a META SQL query to obtain a unique database row. This is one of the overriden methods. For the parameters
     * description please see the most complex execution method {@link #get(Session, Class, Object, Object, int, Map)} .
     */
    @SuppressWarnings("unchecked")
    public <E> E get(final Session session, final Class<E> resultClass, final Object dynamicInputValues)
            throws HibernateException, SqlRuntimeException {
        return get(session, resultClass, dynamicInputValues, null, 0);
    }

    /**
     * Runs a META SQL query to obtain a unique database row. This is one of the overriden methods. For the parameters
     * description please see the most complex execution method {@link #get(Session, Class, Object, Object, int, Map)} .
     */
    @SuppressWarnings("unchecked")
    public <E> E get(final Session session, final Class<E> resultClass, final Object dynamicInputValues,
            final Object staticInputValues) throws HibernateException, SqlRuntimeException {
        return get(session, resultClass, dynamicInputValues, staticInputValues, 0);
    }

    /**
     * Runs a META SQL query to obtain a unique database row. This is one of the overriden methods. For the parameters
     * description please see the most complex execution method {@link #get(Session, Class, Object, Object, int, Map)} .
     */
    @SuppressWarnings("unchecked")
    public <E> E get(final Session session, final Class<E> resultClass, final Object dynamicInputValues,
            final Object staticInputValues, final Map<String, Class<?>> moreResultClasses) throws HibernateException,
            SqlRuntimeException {
        return get(session, resultClass, dynamicInputValues, staticInputValues, 0, moreResultClasses);
    }

    /**
     * Runs a META SQL query to obtain a unique database row. This is one of the overriden methods. For the parameters
     * description please see the most complex execution method {@link #get(Session, Class, Object, Object, int, Map)} .
     */
    @SuppressWarnings("unchecked")
    public <E> E get(final Session session, final Class<E> resultClass, final Object dynamicInputValues,
            final Object staticInputValues, final int maxTimeout) throws HibernateException, SqlRuntimeException {
        return get(session, resultClass, dynamicInputValues, staticInputValues, maxTimeout, null);
    }

    /**
     * Runs a META SQL query to obtain a unique database row. This is the primary and the most complex SQL Processor
     * execution method to obtain a unique instance of result class. Criteria to pickup the correct database row are
     * taken from input values.
     * 
     * @param session
     *            Hibernate session, first level cache and the SQL query execution context
     * @param resultClass
     *            The class used for the return values, the SQL query execution output. This class is also named as the
     *            output class or the transport class, In fact it's a standard POJO class, which must include all the
     *            attributes described in the Mapping rule statement. This class itself and all its subclasses must have
     *            public constructors without any parameters. All the attributes used in the mapping rule statement must
     *            be accessible using public getters and setters. The instance of this class are created on the fly in
     *            the query execution using the reflection API.
     * @param dynamicInputValues
     *            The object used for the SQL statement dynamic parameters. The class of this object is also named as
     *            the input class or the dynamic parameters class. The exact class type isn't important, all the
     *            parameters substituted into the SQL prepared statement are picked up using the reflection API.
     * @param staticInputValues
     *            The object used for the SQL statement static parameters. The class of this object is also named as the
     *            input class or the static parameters class. The exact class type isn't important, all the parameters
     *            injected into the SQL query command are picked up using the reflection API. Compared to
     *            dynamicInputValues input parameters, parameters in this class should't be produced by the end user to
     *            prevent SQL injection threat!
     * @param maxTimeout
     *            The max SQL execution time. This parameter can help to protect production system against ineffective
     *            SQL query commands. The value is in milliseconds.
     * @param moreResultClasses
     *            More result classes used for the return values, like the collections classes or the collections items.
     *            They are used mainly for one-to-one, one-to-many and many-to-many associations.
     * @return The instance of the resultClass.
     * @throws org.hibernate.HibernateException
     *             in the case of any problem with ORM or JDBC
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with input/output values handling
     */
    @SuppressWarnings("unchecked")
    public <E> E get(final Session session, final Class<E> resultClass, final Object dynamicInputValues,
            final Object staticInputValues, final int maxTimeout, final Map<String, Class<?>> moreResultClasses)
            throws HibernateException, SqlRuntimeException {
        if (logger.isDebugEnabled()) {
            logger.debug(">> get, session=" + session + ", resultClass=" + resultClass + ", dynamicInputValues="
                    + dynamicInputValues + ", staticInputValues=" + staticInputValues + ", maxTimeout=" + maxTimeout
                    + ", moreResultClasses=" + moreResultClasses);
        }

        E result = null;

        try {
            result = monitor.run(new SqlMonitor.Runner() {
                public E run() {
                    SqlProcessResult processResult = statement.process(SqlMetaStatement.Type.RETRIEVE,
                            dynamicInputValues, staticInputValues, null, features, classToTypeMap);
                    SQLQuery query = session.createSQLQuery(processResult.getSql().toString());
                    if (maxTimeout > 0)
                        query.setTimeout(maxTimeout);
                    processResult.setQueryParams(session, query);
                    SqlMappingResult mappingResult = SqlMappingRule.merge(mapping, processResult);
                    mappingResult.setQueryResultMapping(resultClass, moreResultClasses, query);

                    Object resultRow = query.uniqueResult();
                    if (resultRow != null) {
                        Object resultValue[] = (resultRow instanceof Object[]) ? (Object[]) (Object[]) resultRow
                                : (new Object[] { resultRow });
                        Map<String, Object> instances = new HashMap<String, Object>();
                        E resultInstance = BeanUtils.getInstance(resultClass);
                        if (resultInstance == null) {
                            throw new SqlRuntimeException("There's problem to instantiate " + resultClass);
                        }
                        mappingResult.setQueryResultData(resultInstance, resultValue, instances, null,
                                moreResultClasses);
                        return resultInstance;
                    } else {
                        return null;
                    }
                }
            }, resultClass);
            return result;
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("<< get, result=" + result);
            }
        }
    }

    /**
     * Runs a META SQL update statement to persist a database row. This is one of the overriden methods. For the
     * parameters description please see the most complex execution method {@link #update(Session, Object, Object, int)}
     * .
     */
    @SuppressWarnings("unchecked")
    public int update(final Session session, final Object dynamicInputValues) throws HibernateException,
            SqlRuntimeException {
        return update(session, dynamicInputValues, null, 0);
    }

    /**
     * Runs a META SQL update statement to persist a database row. This is one of the overriden methods. For the
     * parameters description please see the most complex execution method {@link #update(Session, Object, Object, int)}
     * .
     */
    @SuppressWarnings("unchecked")
    public int update(final Session session, final Object dynamicInputValues, final Object staticInputValues)
            throws HibernateException, SqlRuntimeException {
        return update(session, dynamicInputValues, staticInputValues, 0);
    }

    /**
     * Runs a META SQL update statement to persist a database row. This is the primary and the most complex SQL
     * Processor execution method to persist an instance of input values. Changed values are taken from input values. At
     * the same time criteria to pickup the correct database rows(s) are taken from input values.
     * 
     * @param session
     *            Hibernate session, first level cache and the SQL query execution context
     * @param dynamicInputValues
     *            The object used for the SQL statement dynamic parameters. The class of this object is also named as
     *            the input class or the dynamic parameters class. The exact class type isn't important, all the
     *            parameters substituted into the SQL prepared statement are picked up using the reflection API.
     * @param staticInputValues
     *            The object used for the SQL statement static parameters. The class of this object is also named as the
     *            input class or the static parameters class. The exact class type isn't important, all the parameters
     *            injected into the SQL query command are picked up using the reflection API. Compared to
     *            dynamicInputValues input parameters, parameters in this class should't be produced by the end user to
     *            prevent SQL injection threat!
     * @param maxTimeout
     *            The max SQL execution time. This parameter can help to protect production system against ineffective
     *            SQL query commands. The value is in milliseconds.
     * @return The number of updated database rows.
     * @throws org.hibernate.HibernateException
     *             in the case of any problem with ORM or JDBC
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with input/output values handling
     */
    @SuppressWarnings("unchecked")
    public int update(final Session session, final Object dynamicInputValues, final Object staticInputValues,
            final int maxTimeout) throws HibernateException, SqlRuntimeException {
        if (logger.isDebugEnabled()) {
            logger.debug(">> update, session=" + session + ", dynamicInputValues=" + dynamicInputValues
                    + ", staticInputValues=" + staticInputValues + ", maxTimeout=" + maxTimeout);
        }

        Integer count = null;

        try {
            count = monitor.run(new SqlMonitor.Runner() {
                public Integer run() {
                    SqlProcessResult processResult = statement.process(SqlMetaStatement.Type.UPDATE,
                            dynamicInputValues, staticInputValues, null, features, classToTypeMap);
                    SQLQuery query = session.createSQLQuery(processResult.getSql().toString());
                    if (maxTimeout > 0)
                        query.setTimeout(maxTimeout);
                    processResult.setQueryParams(session, query);

                    return query.executeUpdate();
                }
            }, Integer.class);
            return count;
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("<< update, result=" + count);
            }
        }
    }

    /**
     * Runs a META SQL delete statement to delete a database row. This is one of the overriden methods. For the
     * parameters description please see the most complex execution method {@link #delete(Session, Object, Object, int)}
     * .
     */
    @SuppressWarnings("unchecked")
    public int delete(final Session session, final Object dynamicInputValues) throws HibernateException,
            SqlRuntimeException {
        return delete(session, dynamicInputValues, null, 0);
    }

    /**
     * Runs a META SQL delete statement to delete a database row. This is one of the overriden methods. For the
     * parameters description please see the most complex execution method {@link #delete(Session, Object, Object, int)}
     * .
     */
    @SuppressWarnings("unchecked")
    public int delete(final Session session, final Object dynamicInputValues, final Object staticInputValues)
            throws HibernateException, SqlRuntimeException {
        return delete(session, dynamicInputValues, staticInputValues, 0);
    }

    /**
     * Runs a META SQL delete statement to delete a database row. This is the primary and the most complex SQL Processor
     * execution method to delete the database row(s) based on criteria from input values.
     * 
     * @param session
     *            Hibernate session, first level cache and the SQL query execution context
     * @param dynamicInputValues
     *            The object used for the SQL statement dynamic parameters. The class of this object is also named as
     *            the input class or the dynamic parameters class. The exact class type isn't important, all the
     *            parameters substituted into the SQL prepared statement are picked up using the reflection API.
     * @param staticInputValues
     *            The object used for the SQL statement static parameters. The class of this object is also named as the
     *            input class or the static parameters class. The exact class type isn't important, all the parameters
     *            injected into the SQL query command are picked up using the reflection API. Compared to
     *            dynamicInputValues input parameters, parameters in this class should't be produced by the end user to
     *            prevent SQL injection threat!
     * @param maxTimeout
     *            The max SQL execution time. This parameter can help to protect production system against ineffective
     *            SQL query commands. The value is in milliseconds.
     * @return The number of updated database rows.
     * @throws org.hibernate.HibernateException
     *             in the case of any problem with ORM or JDBC
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with input/output values handling
     */
    @SuppressWarnings("unchecked")
    public int delete(final Session session, final Object dynamicInputValues, final Object staticInputValues,
            final int maxTimeout) throws HibernateException, SqlRuntimeException {
        if (logger.isDebugEnabled()) {
            logger.debug(">> delete, session=" + session + ", dynamicInputValues=" + dynamicInputValues
                    + ", staticInputValues=" + staticInputValues + ", maxTimeout=" + maxTimeout);
        }

        Integer count = null;

        try {
            count = monitor.run(new SqlMonitor.Runner() {
                public Integer run() {
                    SqlProcessResult processResult = statement.process(SqlMetaStatement.Type.DELETE,
                            dynamicInputValues, staticInputValues, null, features, classToTypeMap);
                    SQLQuery query = session.createSQLQuery(processResult.getSql().toString());
                    if (maxTimeout > 0)
                        query.setTimeout(maxTimeout);
                    processResult.setQueryParams(session, query);

                    return query.executeUpdate();
                }
            }, Integer.class);
            return count;
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("<< delete, result=" + count);
            }
        }
    }

    /**
     * Returns the insert statement derived from the META SQL statement. For the parameters description please see the
     * most complex execution method {@link #getSql(Object, Object, org.sqlproc.engine.impl.SqlMetaStatement.Type)} .
     */
    public String getInsertSql(final Object dynamicInputValues, final Object staticInputValues)
            throws HibernateException, SqlRuntimeException {
        return getSql(dynamicInputValues, staticInputValues, SqlMetaStatement.Type.CREATE);
    }

    /**
     * Returns the query select statement derived from the META SQL statement. For the parameters description please see
     * the most complex execution method {@link #getSql(Object, Object, org.sqlproc.engine.impl.SqlMetaStatement.Type)}
     * .
     */
    public String getGetSql(final Object dynamicInputValues, final Object staticInputValues) throws HibernateException,
            SqlRuntimeException {
        return getSql(dynamicInputValues, staticInputValues, SqlMetaStatement.Type.RETRIEVE);
    }

    /**
     * Returns the update statement derived from the META SQL statement. For the parameters description please see the
     * most complex execution method {@link #getSql(Object, Object, org.sqlproc.engine.impl.SqlMetaStatement.Type)} .
     */
    public String getUpdateSql(final Object dynamicInputValues, final Object staticInputValues)
            throws HibernateException, SqlRuntimeException {
        return getSql(dynamicInputValues, staticInputValues, SqlMetaStatement.Type.UPDATE);
    }

    /**
     * Returns the delete statement derived from the META SQL statement. For the parameters description please see the
     * most complex execution method {@link #getSql(Object, Object, org.sqlproc.engine.impl.SqlMetaStatement.Type)} .
     */
    public String getDeleteSql(final Object dynamicInputValues, final Object staticInputValues)
            throws HibernateException, SqlRuntimeException {
        return getSql(dynamicInputValues, staticInputValues, SqlMetaStatement.Type.DELETE);
    }

    /**
     * Because SQL Processor is Data Driven Query engine, every input parameters can produce in fact different SQL
     * statement command. This method can help to identify the exact SQL statement command, which is produced in the
     * background of the SQL Processor execution. The statement is derived from the META SQL statement.
     * 
     * @param dynamicInputValues
     *            The object used for the SQL statement dynamic parameters. The class of this object is also named as
     *            the input class or the dynamic parameters class. The exact class type isn't important, all the
     *            parameters substituted into the SQL prepared statement are picked up using the reflection API.
     * @param staticInputValues
     *            The object used for the SQL statement static parameters. The class of this object is also named as the
     *            input class or the static parameters class. The exact class type isn't important, all the parameters
     *            injected into the SQL query command are picked up using the reflection API. Compared to
     *            dynamicInputValues input parameters, parameters in this class should't be produced by the end user to
     *            prevent SQL injection threat!
     * @param statementType
     *            The type of the statement under consideration. It can be CREATE, RETRIEVE, UPDATE or DELETE.
     * @return The SQL statement command derived from the META SQL statement based in the input parameters.
     * @throws org.hibernate.HibernateException
     *             in the case of any problem with ORM or JDBC
     * @throws org.sqlproc.engine.SqlRuntimeException
     *             in the case of any problem with input/output values handling
     */
    public String getSql(final Object dynamicInputValues, final Object staticInputValues,
            final SqlMetaStatement.Type statementType) throws HibernateException, SqlRuntimeException {
        if (logger.isDebugEnabled()) {
            logger.debug(">> getSql, dynamicInputValues=" + dynamicInputValues + ", staticInputValues="
                    + staticInputValues);
        }

        String sql = null;

        try {
            sql = monitor.run(new SqlMonitor.Runner() {

                public String run() {
                    SqlProcessResult processResult = statement.process(statementType, dynamicInputValues,
                            staticInputValues, null, features, classToTypeMap);
                    return processResult.getSql().toString();
                }
            }, String.class);
            return sql;
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("<< getSql, sql=" + sql);
            }
        }
    }

    /**
     * Returns the name of this META SQL, which uniquely identifies the instance. In the case the META SQL statement and
     * Mapping rule are located in queries.properties, this name is the unique part of the keys in this file. For
     * example for the name ALL in queries.properties there's META SQL statement with the name QRY_ALL and Mapping rule
     * with the name OUT_ALL.
     * 
     * @return The name of the SQL engine instance.
     */
    public String getName() {
        return name;
    }

    /**
     * Returns the SQL Monitor instance for the runtime statistics gathering. By default no runtime statistics gathering
     * is active. So this SQL Monitor is implied in SQL engine constructor in the case the statistics gathering should
     * be engaged.
     * 
     * @return The SQL Monitor instance, which is active for this SQL engine instance.
     */
    public SqlMonitor getMonitor() {
        return monitor;
    }
}
